package service

import (
	"encoding/base64"
	"encoding/json"
	"log"
	"net/http"
	"strconv"

	"gitee.com/anxu/golang-web/src/config"
	"gitee.com/anxu/golang-web/src/models"
	"gitee.com/anxu/golang-web/src/repository"
)

// default value
const (
	enableAutoLogin = false
	identityCookie  = "_identity"
	authTimeout     = 60
)

// User user service
var User *user

func init() {
	auto := config.Config.Get("user.enableAutoLogin", enableAutoLogin).(bool)
	identityCookie := config.Config.Get("user.identityCookie", identityCookie).(string)
	timeout := config.Config.Get("user.authTimeout", authTimeout).(int64)
	User = &user{enableAutoLogin: auto, authTimeout: int(timeout), identityCookie: identityCookie}
}

type user struct {
	enableAutoLogin bool
	authTimeout     int
	identityCookie  string
}

type identity struct {
	ID    int
	Token string
}

// Logout 登出
func (u *user) Logout(w http.ResponseWriter, r *http.Request) bool {
	cookie, err := r.Cookie(u.identityCookie)
	if err != nil {
		return false
	}
	defer http.SetCookie(w, cookie)
	cookie.MaxAge = -1
	return true
}

// LoginByPassword 用户名&密码 登录
func (u *user) LoginByPassword(username, password string, w http.ResponseWriter, r *http.Request) bool {
	user := repository.User.FindByEmail(username)
	if user == nil {
		log.Println("Invalid auth password attempted for user ", username)
		return false
	}

	if !Security.ValidatePassword([]byte(password), []byte(user.Password)) {
		return false
	}

	if u.enableAutoLogin {
		cookie := &http.Cookie{
			Name:   u.identityCookie,
			Value:  u.getCookieString(user),
			Domain: r.Host,
			MaxAge: u.authTimeout,
		}
		w.Header().Add("Set-Cookie", cookie.String())
	}

	Session.Set("user", user.ID)
	Logger.Info(Session.session)
	return true
}

// LoginByCookie 从cookie 登录
func (u *user) LoginByCookie(w http.ResponseWriter, r *http.Request) bool {

	if !u.enableAutoLogin {
		return false
	}

	ident := u.getIdentityFromCookie(w, r)

	if ident == nil {
		return false
	}

	user := repository.User.FindById(ident.ID)
	if user == nil {
		Logger.Info("Invalid auth token attempted for user" + strconv.Itoa(ident.ID) + " : " + ident.Token)
		return false
	}

	if !user.ValidateToken(ident.Token) {
		cookie := &http.Cookie{
			Name:   u.identityCookie,
			Value:  "",
			MaxAge: -1,
		}
		w.Header().Add("Set-Cookie", cookie.String())
		return false
	}
	Session.Set("user", user.ID)
	Logger.Info(user.Name + " logined from cookie")
	return true
}

func (u *user) getIdentityFromCookie(w http.ResponseWriter, r *http.Request) *identity {
	cookie, err := r.Cookie(u.identityCookie)
	if err != nil {
		return nil
	}

	str, err := base64.StdEncoding.DecodeString(cookie.Value)
	if err != nil {
		log.Fatalln(err)
	}
	str = Security.Decrypt(str)
	var ident = &identity{}
	err = json.Unmarshal(str, ident)
	if err != nil {
		log.Println(err)
		return nil
	}
	return ident
}

func (u *user) getCookieString(user *models.User) string {
	var ident = identity{ID: user.ID, Token: user.GetToken()}
	str, _ := json.Marshal(ident)
	str = Security.Encrypt(str)

	return base64.StdEncoding.EncodeToString(str)
}

func (u *user) Create(email, name, password string) *models.User {

	if user := repository.User.FindByEmail(email); user != nil {
		return nil
	}
	password = string(Security.GeneratePasswordHash([]byte(password)))
	user := &models.User{Name: name, Email: email, Password: password}
	res := models.Db.Create(user)
	if res.RowsAffected > 0 {
		return user
	}
	return nil
}
